# -------------------------------
# 导入标准库
# -------------------------------
import os  # 用于访问操作系统变量，比如环境变量

# -------------------------------
# 导入第三方库
# -------------------------------
from dotenv import load_dotenv  # 用于加载 .env 文件里的环境变量
from fastapi import FastAPI     # FastAPI 是一个现代、高性能的 Web 框架
from fastapi.responses import RedirectResponse  # 用于返回重定向响应
from langchain_core.output_parsers import StrOutputParser  # LangChain 的字符串解析器
from langchain_core.prompts import ChatPromptTemplate       # LangChain 的 Prompt 模板
from langchain_openai import ChatOpenAI                     # LangChain 对 OpenAI 接口的封装
from langserve import add_routes                            # 用于快速给 FastAPI 添加 LangChain 路由


# -------------------------------
# 1. 加载环境变量
# -------------------------------
# load_dotenv() 会自动读取项目根目录下的 `.env` 文件，
# 把里面定义的变量（如 MODELSCOPE_OPENAI_API_KEY=xxxxxx）
# 加载到系统环境变量中，这样就可以用 os.getenv() 获取。
# 注意：.env 文件应该放在 .gitignore 里，防止上传到 Git 仓库。
load_dotenv()

# 从系统环境变量中获取 API 密钥。
# 这里不要直接写在代码里，否则别人能在开源仓库中看到。
api_key = os.getenv("MODELSCOPE_OPENAI_API_KEY")


# -------------------------------
# 2. 创建 FastAPI 应用
# -------------------------------
app = FastAPI(
    title="LangChain Server",      # 文档标题
    version="1.0",                 # 服务版本
    description="一个简单的API服务，使用langchain Runnable 接口",  # 简介
)


# -------------------------------
# 3. 定义一个函数用于创建 LangChain 链（chain）
# -------------------------------
def create_chain():
    """
    构建一个最简单的问答链（prompt → LLM → 输出解析）。
    """

    # 1️⃣ 创建提示模板
    # ChatPromptTemplate 是 LangChain 提供的 Prompt 封装方式，
    # 支持变量替换。例如：用户输入 question，会自动填入模板。
    prompt = ChatPromptTemplate.from_template(
        "请回答以下问题：{question}"
    )

    # 2️⃣ 创建大模型接口
    # ChatOpenAI 是 LangChain 中的通用大模型类。
    # 这里我们不是用 OpenAI，而是用 ModelScope（魔搭社区）的接口，
    # 所以自定义 api_base_url。
    llm = ChatOpenAI(
        api_base_url="https://api-inference.modelscope.cn/v1",  # 模型推理服务地址
        api_key=api_key,                                        # 从 .env 获取的 API 密钥
        # model="Qwen/Qwen3-14B",                               # 可以切换不同模型
        model="Qwen/Qwen2.5-VL-7B-Instruct",                    # 当前使用的视觉语言模型
        streaming=True,                                         # 是否启用流式输出（边生成边返回）
        temperature=0.7                                         # 控制回答的随机性，越低越稳定
    )

    # 3️⃣ 创建输出解析器
    # StrOutputParser 会把模型输出解析为纯文本字符串。
    output_parser = StrOutputParser()

    # 4️⃣ 构建链
    # 使用“|”操作符把 prompt、llm、output_parser 串起来形成一个完整的链。
    # 执行时会自动把输入 question 传入模板，再送入 LLM，最后解析为字符串。
    chain = prompt | llm | output_parser
    return chain


# -------------------------------
# 4. 创建真正的 Runnable 对象
# -------------------------------
# LangServe 需要传入一个可运行的 LangChain 对象。
chain = create_chain()


# -------------------------------
# 5. 把 chain 添加到 FastAPI 路由中
# -------------------------------
# add_routes 会自动为你创建 REST 接口，
# 你可以用 POST /chat 发送 JSON，如 {"question": "你好"}。
add_routes(
    app,
    chain,              # ✅ 要暴露的 Runnable 对象
    path="/chat",       # API 路径
)


# -------------------------------
# 6. 添加一个首页跳转接口
# -------------------------------
@app.get("/")
async def redirect_root_to_docs() -> RedirectResponse:
    """
    当访问根路径 / 时，自动跳转到 /docs（Swagger UI 页面）
    """
    return RedirectResponse("/docs")


# -------------------------------
# 7. 启动应用（开发模式）
# -------------------------------
if __name__ == "__main__":
    import uvicorn  # Uvicorn 是 ASGI 服务器，用来运行 FastAPI

    # 启动服务，监听 8000 端口
    uvicorn.run(app, host="0.0.0.0", port=8000)
